package com.tianquan.miracle.util;

import com.tianquan.miracle.context.ApplicationContext;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author: tianquan
 * @date: 2024-09-30  09:08
 * @Description: SPI实现工具类
 */
public class ServiceClassLoader {

    private static ApplicationContext applicationContext;

    /**
     * 初始化ApplicationContext。
     *
     * @param applicationContext 应用上下文
     */
    public static void init(ApplicationContext applicationContext) {
        ServiceClassLoader.applicationContext = applicationContext;
    }

    private static final String CLASSPATH = "META-INF/miracle";

    /**
     * 获取META-INF/miracle文件夹。
     *
     * @return META-INF/miracle文件夹
     */
    private static File getResourcesFile() {
        return new File(Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(CLASSPATH)).getFile());
    }

    /**
     * 根据接口的Class获取META-INF/miracle下对应的实现类，并且实例化到容器中。
     *
     * @param clazz 接口的Class
     * @return 实现类的列表
     */
    public static <T> List<T> loaderService(Class<T> clazz) {
        List<T> list = null;
        String interfaceName = clazz.getCanonicalName();
        // 基于IO流获取Resource下的META-INF/miracle下的文件
        try {
            File file = getResourcesFile();
            if (!file.isDirectory()) {
                return null;
            }
            File[] files = file.listFiles();
            if (files == null || files.length == 0) {
                return null;
            }
            for (File f : files) {
                String fileName = f.getName();
                // 找到要加载的文件
                if (!interfaceName.equals(fileName)) {
                    continue;
                }
                // 加载使用方这个文件下的实现类
                BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
                Set<String> clazzInstanceSet = bufferedReader.lines().collect(Collectors.toSet());
                if (clazzInstanceSet.isEmpty()) {
                    return null;
                }
                list = (List<T>) load(clazzInstanceSet, interfaceName);
                break;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    /**
     * 根据接口的Class获取META-INF/miracle下对应的实现全类名。
     *
     * @param clazz 接口的Class
     * @return 实现类的全类名集合
     */
    public static Set<String> getClassName(Class<?> clazz) {
        String interfaceName = clazz.getCanonicalName();
        // 基于IO流获取Resource下的META-INF/miracle下的文件
        try {
            File file = getResourcesFile();
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                if (files == null || files.length == 0) {
                    return null;
                }
                for (File f : files) {
                    String fileName = f.getName();
                    // 找到要加载的文件
                    if (!interfaceName.equals(fileName)) {
                        continue;
                    }
                    // 加载使用方这个文件下的实现类
                    BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
                    Set<String> clazzInstanceSet = bufferedReader.lines().collect(Collectors.toSet());
                    if (clazzInstanceSet.isEmpty()) {
                        return null;
                    }
                    return clazzInstanceSet;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 从clazzInstanceSet里获取实现了interfaceName接口的实现类。
     *
     * @param clazzInstanceSet 实现类的全类名集合
     * @param interfaceName 接口的全类名
     * @return 实现类的实例列表
     * @throws Exception 如果实例化过程中发生异常
     */
    private static List<Object> load(Set<String> clazzInstanceSet, String interfaceName) throws Exception {
        List<Object> list = new ArrayList<>();
        for (String clazzInstanceName : clazzInstanceSet) {
            // 准备实例化
            Class<?> aClass = MiracleUtils.getClassLoader().loadClass(clazzInstanceName);
            // 获取到类实现的接口或继承的父类
            Class<?>[] interfaces = aClass.getInterfaces();
            boolean isExist = false;
            for (Class<?> anInterface : interfaces) {
                String parentInterfaceName = anInterface.getCanonicalName();
                if (interfaceName.equals(parentInterfaceName)) {
                    isExist = true;
                    break;
                }
            }
            if (!isExist) {
                throw new RuntimeException(clazzInstanceName + "class must implements " + interfaceName);
            }
            // 无参构造实例化
            Constructor<?> constructor = aClass.getConstructor();
            String beanName = MiracleUtils.createBeanName(aClass);
            Object bean = constructor.newInstance();
            // 放入容器
            applicationContext.registerBean(beanName, bean);
            list.add(bean);
        }
        return list;
    }

}
